package modeljs

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"sync"

	"gitee.com/haodreams/golib/easyjs/taskjs"
	"gitee.com/haodreams/golib/easyroute/group"
	"gitee.com/haodreams/golib/logs"
	"github.com/dop251/goja"
	"github.com/gin-gonic/gin"
)

//用于接收上传的数据

type FileParse struct {
	lastModiTime int64
	path         string
	name         string
	data         []byte
}

// 动态处理js数据
type ModeJs struct {
	group.Group
	path     string //需要处理的模型的路径
	lock     sync.Mutex
	mapParse map[string]*FileParse
}

// 新建数据模型
func NewModel(modelDir ...string) *ModeJs {
	m := new(ModeJs)
	m.path = "js/models"
	if len(modelDir) > 0 && modelDir[0] != "" {
		m.path = modelDir[0]
	}
	m.path = strings.TrimSuffix(m.path, "/")

	m.mapParse = make(map[string]*FileParse)
	return m
}

func loadFile(path string) (data []byte, modiTime int64, err error) {
	data, err = os.ReadFile(path)
	if err != nil {
		return nil, 0, err
	}
	st, err := os.Stat(path)
	modiTime = st.ModTime().Unix()
	return
}

func (m *ModeJs) getParse(ctx *gin.Context, file string) []byte {
	//判断模型是否存在
	m.lock.Lock()
	parse, ok := m.mapParse[file]
	m.lock.Unlock()
	if !ok {
		//如果解析的文件不存在，再磁盘上再检查一次，如果有则更新，否则返回模型不存在提示
		path := fmt.Sprintf("%s/%s.js", m.path, file)
		data, modiTime, err := loadFile(path)
		if err != nil {
			m.Msg(ctx, "模型文件读取失败", http.StatusInternalServerError)
			return nil
		}
		parse = &FileParse{
			lastModiTime: modiTime,
			path:         path,
			name:         file,
			data:         data,
		}
		m.lock.Lock()
		m.mapParse[file] = parse
		m.lock.Unlock()
	}

	st, err := os.Stat(parse.path)
	if err != nil {
		m.Msg(ctx, "模型文件不存在", http.StatusInternalServerError)
		return nil
	}

	if st.ModTime().Unix() != parse.lastModiTime {
		data, modiTime, err := loadFile(parse.path)
		if err != nil {
			m.Msg(ctx, "模型文件读取失败", http.StatusInternalServerError)
			return nil
		}
		parse.data = data
		parse.lastModiTime = modiTime
	}

	return parse.data
}

func (m *ModeJs) run(ctx *gin.Context, isPage bool) (result string, err error) {
	params := m.Params(ctx)
	file := params.GetTrimString("model")
	if file == "" {
		m.Msg(ctx, "请指定模型", http.StatusBadRequest)
		return
	}
	var runParams any
	if isPage {
		if m.IsPost(ctx) {
			runParams = params.Form(ctx.Request)
		}
	} else {
		if m.IsPost(ctx) {
			var data []byte
			data, err = io.ReadAll(ctx.Request.Body)
			ctx.Request.Body.Close()
			if err != nil {
				return
			}

			if ctx.Request.Header.Get("Content-Type") == "application/json" {
				runParams = string(data)
			} else {
				runParams = data
			}
		} else {
			runParams = params.GetTrimString("param")
		}
	}

	data := m.getParse(ctx, file)
	if data == nil {
		return
	}

	//执行js脚本的代码
	vm, err := taskjs.LoadJsString(file, string(data))
	if err != nil {
		m.Msg(ctx, "模型加载失败", http.StatusInternalServerError)
		return
	}

	//自动检查是否有初始化函数
	setup, ok := goja.AssertFunction(vm.Get("setup"))
	if ok && setup != nil {
		_, err = setup(nil, vm.ToValue(runParams))
		if err != nil {
			logs.Warn(err.Error())
			m.Msg(ctx, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	run, ok := goja.AssertFunction(vm.Get("run"))
	if !ok {
		err = errors.New("run  not a function")
		logs.Warn(err)
		return
	}
	r, err := run(nil, vm.ToValue(runParams))
	if err != nil{
		return
	}
	if r == nil{
		return "", err
	}
	return r.String(), err
}

// 处理各类不同的数据格式(自定义解析)
func (m *ModeJs) Model(ctx *gin.Context) {
	result, err := m.run(ctx, false)
	if err != nil {
		logs.Warn(err.Error())
		m.Msg(ctx, err.Error(), http.StatusInternalServerError)
		return
	}
	m.Group.Data(ctx, result)
}

// 返回包含业务标识的数据
func (m *ModeJs) Data(ctx *gin.Context) {
	result, err := m.run(ctx, false)
	if err != nil {
		logs.Warn(err.Error())
		m.Msg(ctx, err.Error(), http.StatusInternalServerError)
		return
	}

	switch {
	case strings.HasPrefix(result, "json:"):
		m.Group.Data(ctx, result[5:])
	case strings.HasPrefix(result, "html:"):
		ctx.Header("Content-Type", "text/html")
		ctx.Writer.Write([]byte(result[5:]))
	case strings.HasPrefix(result, "xml:"):
		ctx.Header("Content-Type", "text/xml")
		ctx.Writer.Write([]byte(result[4:]))
	case strings.HasPrefix(result, "text:"):
		ctx.Header("Content-Type", "text/plain")
		ctx.Writer.Write([]byte(result[5:]))
	default:
		ctx.Header("Content-Type", "application/json")
		ctx.Writer.Write([]byte(result))
	}
}

// 返回包含业务标识的数据
func (m *ModeJs) PageData(ctx *gin.Context) {
	result, err := m.run(ctx, true)
	if err != nil {
		logs.Warn(err.Error())
		m.Msg(ctx, err.Error(), http.StatusInternalServerError)
		return
	}

	ctx.Header("Content-Type", "application/json")
	ctx.Writer.Write([]byte(result))
}
